/**
 * Moondance UML modeling tool
 * 
 * This program is free software; you can redistribute it and
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package ro.pub.cs.se.moondance.model;

import java.util.ArrayList;
import java.util.Arrays;

import org.eclipse.draw2d.Graphics;
import org.eclipse.ui.views.properties.ComboBoxPropertyDescriptor;
import org.eclipse.ui.views.properties.IPropertyDescriptor;

import ro.pub.cs.se.moondance.model.ModelElement;
import ro.pub.cs.se.moondance.model.UMLEntity;
import ro.pub.cs.se.moondance.model.UMLClass;
import ro.pub.cs.se.moondance.model.UMLInterface;

/**
 * A connection between two distinct UML entities.
 */
public abstract class Connection extends ModelElement {
	public static final Integer ASSOCIATION = new Integer(11);
	public static final Integer DIRECTED_ASSOCIATION = new Integer(12);
	public static final Integer AGGREGATION = new Integer(13);
	public static final Integer COMPOSITION = new Integer(14);
	public static final Integer INHERITANCE = new Integer(15);
	public static final Integer REALIZATION = new Integer(16);

	public static final String ASSOCIATION_NAME = "connected";
	public static final String DIRECTED_ASSOCIATION_NAME = "direct connected";
	public static final String AGGREGATION_NAME = "has a";
	public static final String COMPOSITION_NAME = "strong connectioned";
	public static final String INHERITANCE_NAME = "is a";
	public static final String REALIZATION_NAME = "realizes";

	public static final String ONE_ONLY = "1";
	public static final String ONE_OR_MORE = "1..*";
	public static final String ZERO_OR_MORE = "0..*";
	public static final String ZERO_OR_MORE_SHORT = "*";
	public static final String ZERO_OR_ONE = "0..1";

	private static String multiplicityValues[] = { ONE_ONLY, ONE_OR_MORE,
			ZERO_OR_MORE, ZERO_OR_MORE_SHORT, ZERO_OR_ONE };

	public static ArrayList<String> multiplicityValuesList = new ArrayList<String>(
			Arrays.asList(multiplicityValues));

	/**
	 * Used for indicating that a Connection with solid line style should be
	 * created.
	 * 
	 * @see ro.pub.cs.se.moondance.parts.UMLEditPart#createEditPolicies()
	 */
	public static final Integer SOLID_CONNECTION = new Integer(
			Graphics.LINE_SOLID);

	/**
	 * Used for indicating that a Connection with dashed line style should be
	 * created.
	 * 
	 * @see ro.pub.cs.se.moondance.parts.UMLEditPart#createEditPolicies()
	 */
	public static final Integer DASHED_CONNECTION = new Integer(
			Graphics.LINE_DASH);

	/** Property ID to use when the line style of this connection is modified. */
	public static final String LINESTYLE_PROP = "LineStyle";

	private static final IPropertyDescriptor[] descriptors = new IPropertyDescriptor[1];

	private static final String SOLID_STR = "Solid";

	private static final String DASHED_STR = "Dashed";

	private static final long serialVersionUID = 1;

	/** True, if the connection is attached to its endpoints. */
	private boolean isConnected;

	/** Line drawing style for this connection. */
	private int lineStyle = Graphics.LINE_SOLID;

	/** Connection's source end point. */
	private UMLEntity source;

	/** Connection's target end point. */
	private UMLEntity target;

	/** Connection type. */
	protected Integer type;

	/** Connection name and multiplicity. */
	protected String name;
	protected String sourceMultiplicity = Connection.ONE_OR_MORE;
	protected String targetMultiplicity = Connection.ONE_ONLY;

	static {
		descriptors[0] = new ComboBoxPropertyDescriptor(LINESTYLE_PROP,
				LINESTYLE_PROP, new String[] { SOLID_STR, DASHED_STR });
	}

	/**
	 * Create a (solid) connection between two distinct UML blocks.
	 * 
	 * @param source
	 *            a source endpoint for this connection (non null)
	 * @param target
	 *            a target endpoint for this connection (non null)
	 * @param type
	 *            of the connection: ASSOCIATION, DIRECTED_ASSOCIATION,
	 *            AGGREGATION, COMPOSITION, INHERITANCE, REALIZATION
	 * @throws IllegalArgumentException
	 *             if any of the parameters are null or source == target
	 * @see #setLineStyle(int)
	 */
	public Connection(UMLEntity source, UMLEntity target, Integer type) {
		this.setType(type);
		if (type.equals(ASSOCIATION) || type.equals(DIRECTED_ASSOCIATION)
				|| type.equals(AGGREGATION) || type.equals(COMPOSITION)
				|| type.equals(INHERITANCE))
			setLineStyle(SOLID_CONNECTION);
		else if (type.equals(REALIZATION)) {
			setLineStyle(DASHED_CONNECTION);
		} else
			// Unknown connection type
			;

		reconnect(source, target);
	}

	/**
	 * Disconnect this connection from the UML it is attached to.
	 */
	public void disconnect() {
		if (isConnected) {
			source.removeConnection(this);
			target.removeConnection(this);
			if (type.equals(INHERITANCE))
				((UMLClass) source).removeExtendedClass((UMLClass) target);
			else if (type.equals(REALIZATION))
				((UMLClass) source)
						.removeImplementedInterface((UMLInterface) target);
			isConnected = false;
		}
	}

	/**
	 * Returns the line drawing style of this connection.
	 * 
	 * @return an int value (Graphics.LINE_DASH or Graphics.LINE_SOLID)
	 */
	public int getLineStyle() {
		return lineStyle;
	}

	/**
	 * Returns the descriptor for the lineStyle property
	 * 
	 * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors()
	 */
	public IPropertyDescriptor[] getPropertyDescriptors() {
		return descriptors;
	}

	/**
	 * Returns the lineStyle as String for the Property Sheet
	 * 
	 * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java.lang.Object)
	 */
	public Object getPropertyValue(Object id) {
		System.out.println("Ajung in getPropertyValue");
		if (id.equals(LINESTYLE_PROP)) {
			if (getLineStyle() == Graphics.LINE_DASH)
				// Dashed is the second value in the combo dropdown
				return new Integer(1);
			// Solid is the first value in the combo dropdown
			return new Integer(0);
		}
		return super.getPropertyValue(id);
	}

	/**
	 * Returns the source endpoint of this connection.
	 * 
	 * @return a non-null Shape instance
	 */
	public UMLEntity getSource() {
		return source;
	}

	/**
	 * Returns the target endpoint of this connection.
	 * 
	 * @return a non-null Shape instance
	 */
	public UMLEntity getTarget() {
		return target;
	}

	/**
	 * Reconnect this connection. The connection will reconnect with the UML it
	 * was previously attached to.
	 */
	public void reconnect() {
		if (!isConnected) {
			source.addConnection(this);
			target.addConnection(this);
			if (type.equals(INHERITANCE))
				((UMLClass) source).addExtendedClass((UMLClass) target);
			else if (type.equals(REALIZATION))
				((UMLClass) source)
						.addImplementedInterface((UMLInterface) target);
			isConnected = true;
		}
	}

	/**
	 * Reconnect to a different source and/or target shape. The connection will
	 * disconnect from its current attachments and reconnect to the new source
	 * and target.
	 * 
	 * @param newSource
	 *            a new source endpoint for this connection (non null)
	 * @param newTarget
	 *            a new target endpoint for this connection (non null)
	 * @throws IllegalArgumentException
	 *             if any of the paramers are null or newSource == newTarget
	 */
	public void reconnect(UMLEntity newSource, UMLEntity newTarget) {
		if (newSource == null || newTarget == null || newSource == newTarget) {
			throw new IllegalArgumentException();
		}
		disconnect();
		this.source = newSource;
		this.target = newTarget;
		reconnect();
	}

	/**
	 * Set the line drawing style of this connection.
	 * 
	 * @param lineStyle
	 *            one of following values: Graphics.LINE_DASH or
	 *            Graphics.LINE_SOLID
	 * @see Graphics#LINE_DASH
	 * @see Graphics#LINE_SOLID
	 * @throws IllegalArgumentException
	 *             if lineStyle does not have one of the above values
	 */
	public void setLineStyle(int lineStyle) {
		if (lineStyle != Graphics.LINE_DASH && lineStyle != Graphics.LINE_SOLID) {
			throw new IllegalArgumentException();
		}
		this.lineStyle = lineStyle;
		firePropertyChange(LINESTYLE_PROP, null, new Integer(this.lineStyle));
	}

	/**
	 * Sets the lineStyle based on the String provided by the PropertySheet
	 * 
	 * @see org.eclipse.ui.views.properties.IPropertySource#setPropertyValue(java.lang.Object,
	 *      java.lang.Object)
	 */
	public void setPropertyValue(Object id, Object value) {
		if (id.equals(LINESTYLE_PROP))
			setLineStyle(new Integer(1).equals(value) ? Graphics.LINE_DASH
					: Graphics.LINE_SOLID);
		else
			super.setPropertyValue(id, value);
	}

	public void setType(Integer type) {
		this.type = type;
	}

	public Integer getType() {
		return type;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getSourceMultiplicity() {
		return sourceMultiplicity;
	}

	public void setSourceMultiplicity(String multiplicity) {
		this.sourceMultiplicity = multiplicity;
	}

	public String getTargetMultiplicity() {
		return targetMultiplicity;
	}

	public void setTargetMultiplicity(String multiplicity) {
		this.targetMultiplicity = multiplicity;
	}
}